package com.aliyun.messagemgt.application.service.Impl;


import com.alibaba.cloudapi.sdk.core.model.ApiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.messagemgt.application.service.ChannelService;
import com.aliyun.messagemgt.application.service.PushChannelMap;
import com.aliyun.messagemgt.application.service.PushService;
import com.aliyun.messagemgt.common.annotation.PagingAnnotation;
import com.aliyun.messagemgt.common.dto.send.MsgRecordVO;
import com.aliyun.messagemgt.common.dto.send.PushChannelInfo;
import com.aliyun.messagemgt.common.dto.send.Receiver;
import com.aliyun.messagemgt.common.enums.ChannelTypeEnum;
import com.aliyun.messagemgt.common.enums.MediaLimitEnum;
import com.aliyun.messagemgt.common.enums.RangeTypeEnum;
import com.aliyun.messagemgt.common.errorcode.CommonErrorCode;
import com.aliyun.messagemgt.common.exceptions.BaseException;
import com.aliyun.messagemgt.converter.PushChannelConverter;
import com.aliyun.messagemgt.domain.repository.ISysPropertyRepository;
import com.aliyun.messagemgt.domain.repository.MsgSourceRepository;
import com.aliyun.messagemgt.domain.repository.PushChannelRepository;
import com.aliyun.messagemgt.dto.PushChannelAdd;
import com.aliyun.messagemgt.dto.PushChannelList;
import com.aliyun.messagemgt.dto.base.FourAObjectEntity;
import com.aliyun.messagemgt.dto.out.MsgPushResponseDTO;
import com.aliyun.messagemgt.dto.query.PushChannelQuery;
import com.aliyun.messagemgt.dto.sys.SysPropertyDTO;
import com.aliyun.messagemgt.dto.vo.ChannelType;
import com.aliyun.messagemgt.dto.vo.MsgBody;
import com.aliyun.messagemgt.dto.vo.MsgVO;
import com.aliyun.messagemgt.sendplugin.SendStrategyContext;
import com.aliyun.messagemgt.util.SyncApiListClient;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author
 * @date
 */
@Service
public class ChannelServiceImpl implements ChannelService {

    private static Logger logger = LoggerFactory.getLogger(ChannelServiceImpl.class);

    private static final int MIN_SUCCESS_CODE = 200;

    private static final int MAX_SUCCESS_CODE = 300;

    /**
     * 测试系统端接口是否联通返回错误码。由于消息源ID不可用，只要返回这些说明已经联通
     */
    private static final Integer[] TEST_CON_ERROR_CODE = {10001, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 2001, 2002, 3001};

    @Value("${channel.type}")
    private String channelType;

    @Value("${channel.shieldType}")
    private List<String> shieldKey;

    @Autowired
    PushChannelRepository pushChannelRepository;
    @Autowired
    MsgSourceRepository msgSourceRepository;
    @Autowired
    private ISysPropertyRepository sysPropertyRepository;
    @Autowired
    private PushChannelMap pushChannelMap;
    @Autowired
    private SendStrategyContext sendStrategyContext;
    @Autowired
    private PushService pushService;

    @Override
    public Object savePushChannel(PushChannelAdd pushChannelAdd) {
        checkParams(pushChannelAdd);
        int insert = pushChannelRepository.insertPushChannel(pushChannelAdd);
        pushChannelMap.setChannelMap();
        return insert;
    }


    @Override
    public Object commitPushChannel(PushChannelAdd pushChannelAdd) {
        checkParams(pushChannelAdd);
        int insert = pushChannelRepository.insertPushChannel(pushChannelAdd);
        pushChannelMap.setChannelMap();
        return insert;
    }

    @Override
    @PagingAnnotation
    public PageInfo<PushChannelList> listPushChannel(PushChannelQuery pushChannelQuery) {
        List<PushChannelList> resultName = new ArrayList<>();

        List<PushChannelList> resultList = pushChannelRepository.listPushChannel(pushChannelQuery);
        List<PushChannelList> pushChannelLists = pushChannelRepository.queryTopChannel(pushChannelQuery);
        List<PushChannelList> pushChannelLists1 = pushChannelRepository.listPushChannelNoTop(pushChannelQuery);
        List<PushChannelList> objects = new ArrayList<>();
        if (resultList.isEmpty() && !StringUtils.isBlank(pushChannelQuery.getChannelName())) {
            resultName = pushChannelRepository.queryByChannelCode(pushChannelQuery.getChannelName());
        }
        resultList.clear();
        objects.addAll(pushChannelLists);
        objects.addAll(pushChannelLists1);
        objects.addAll(resultName);

        PageInfo pageInfo = new PageInfo(resultList);

        Integer pageNum = pushChannelQuery.getPageNum();
        Integer pageSize = pushChannelQuery.getPageSize();
        if (objects.size() < 5 + (pageNum - 2) * pageSize) {
            pageInfo.setList(resultList);
            pageInfo.setTotal(objects.size());
            return pageInfo;
        }
        List<PushChannelList> pushChannelLists2 = objects.subList(5 + (pageNum - 2) * pageSize, objects.size());
        if (pushChannelLists2.size() > pageSize) {
            pushChannelLists2 = pushChannelLists2.subList(0, pageSize);
        } else {
            pushChannelLists2 = pushChannelLists2.subList(0, pushChannelLists2.size());
        }
        resultList.addAll(pushChannelLists2);
        //添加数据权限字段，0 可见 1 具有完全的操作权（为管理员）
        dealDataPrivilege(resultList, pushChannelQuery);
        dealOwnerFiled(resultList);
        pageInfo.setList(resultList);
        pageInfo.setTotal(objects.size());
        return pageInfo;
    }

    private List<PushChannelList> queryTopChannel(PushChannelQuery pushChannelQuery) {
        Integer pageNum = pushChannelQuery.getPageNum();
        Integer pageSize = pushChannelQuery.getPageSize();
        List<PushChannelList> pushChannelLists = pushChannelRepository.queryTopChannel(pushChannelQuery);
        if (pushChannelQuery.getPageNum() == 1) {
            return pushChannelLists;
        } else {
            if (pushChannelLists.size() >= 5 + (pageNum - 2) * pageSize) {
                List<PushChannelList> pushChannelLists1 = pushChannelLists.subList(((pageNum - 2) * pageSize + 5), pushChannelLists.size());
                return pushChannelLists1;
            } else {
                pushChannelLists.clear();
                return pushChannelLists;
            }
        }
    }

    private void dealOwnerFiled(List<PushChannelList> resultList) {
        for (PushChannelList pushChannelList : resultList) {
            String owner = pushChannelList.getOwner();
            if (owner != null) {
                try {
                    List<Map> maps = JSONArray.parseArray(owner, Map.class);
                    pushChannelList.setOwners(maps);
                } catch (Exception e) {
                    logger.info("管理员字段异常 {}", e.toString());
                    continue;
                }
            }
        }
    }


    private void dealDataPrivilege(List<PushChannelList> resultList, PushChannelQuery pushChannelQuery) {
        for (PushChannelList pc : resultList) {
            String owner = pc.getOwner();
            try {
                if (null != owner && owner.length() > 0) {
                    List<Map> maps = JSONArray.parseArray(owner, Map.class);
                    for (Map map : maps) {
                        Object key = map.get("key");
                        if (key.equals(pushChannelQuery.getUserId())) {
                            pc.setDataPrivilege(1);
                        } else {
                            //pc.setDataPrivilege(0);
                            pc.setDataPrivilege(1);
                            if (pushChannelQuery.getRole() == 1) {
                                pc.setDataPrivilege(1);
                            }
                        }
                    }
                } else {
//                    pc.setDataPrivilege(0);
                    pc.setDataPrivilege(1);
                    if (pushChannelQuery.getRole() == 1) {
                        pc.setDataPrivilege(1);
                    }
                }
            } catch (Exception e) {
                logger.info("处理管理员字段时出现异常 {}", e.toString());
                continue;
            }
        }
    }

    @Override
    public Object topChannel(PushChannelQuery pushChannelQuery) {
        Integer topStatus = pushChannelQuery.getTopStatus();
        if (pushChannelQuery.getPushChannelId() == null || topStatus == null) {
            throw new BaseException(CommonErrorCode.COMMON_PARAM_INVALID);
        }
        if (topStatus == 1 || topStatus == 0) {
            int i = pushChannelRepository.topPushChannel(pushChannelQuery);
            if (i < 1) {
                throw new RuntimeException("操作失败");
            }
            return i;
        } else {
            throw new BaseException(CommonErrorCode.COMMON_PARAM_INVALID);
        }

    }

    @Override
    public Object delPushChannel(PushChannelQuery pushChannelQuery) {
        if (pushChannelQuery.getPushChannelId() == null) {
            throw new BaseException(CommonErrorCode.COMMON_PARAM_INVALID);
        }
        //先查询该渠道是否被消息源所使用
        int state = msgSourceRepository.queryChannelState(pushChannelQuery.getPushChannelId());
        if (state > 0) {
            throw new RuntimeException("该渠道已被使用，不可删除");
        }
        int i = pushChannelRepository.delPushChannel(pushChannelQuery);
        if (i < 1) {
            throw new RuntimeException("删除失败");
        }
        pushChannelMap.setChannelMap();
        return i;
    }

    @Override
    public Object modPushChannel(PushChannelAdd pushChannelAdd) {

        if (pushChannelAdd.getPushChannelId() == null) {
            throw new BaseException(CommonErrorCode.COMMON_PARAM_INVALID);
        }
        checkParams(pushChannelAdd);
        pushChannelAdd.setUserUpdate(pushChannelAdd.getUserName());
        int i = pushChannelRepository.modPushChannel(pushChannelAdd);
        if (i < 1) {
            throw new RuntimeException("修改失败");
        }
        pushChannelMap.setChannelMap();
        return i;
    }

    @Override
    public Object detailChannel(PushChannelQuery pushChannelQuery) {
        if (pushChannelQuery.getPushChannelId() == null) {
            throw new BaseException(CommonErrorCode.COMMON_PARAM_INVALID);
        }
        PushChannelList pushChannelList = pushChannelRepository.queryChannelDetail(pushChannelQuery.getPushChannelId());
        String owner = pushChannelList.getOwner();
        if (owner != null) {
            List<Map> fourAObjectEntities = JSONArray.parseArray(owner, Map.class);
            pushChannelList.setOwners(fourAObjectEntities);
        }
        return pushChannelList;
    }

    private void checkParams(PushChannelAdd pushChannelAdd) {
        int i = pushChannelRepository.selectByDescription(pushChannelAdd);
        if (i > 0) {
            throw new BaseException(CommonErrorCode.FAIL_SAVE_PUSHCHANNEL);
        }
        int x = pushChannelRepository.selectByCode(pushChannelAdd);
        if (x > 0) {
            throw new BaseException(CommonErrorCode.CODING_REPEAT);
        }
        dealOwner(pushChannelAdd);
    }

    private void dealOwner(PushChannelAdd pushChannelAdd) {
        List<FourAObjectEntity> owners = pushChannelAdd.getOwners();
        String owner = JSONArray.toJSONString(owners);
        pushChannelAdd.setOwner(owner);
        pushChannelAdd.setUserCreate(pushChannelAdd.getUserName());
    }

    @Override
    public int testConnection(PushChannelAdd pushChannelAdd) {
        if (6 != pushChannelAdd.getChannelType()) {
            return 1;
        } else {
            String apiName = pushChannelAdd.getApiName();
            String apiUrl = pushChannelAdd.getApiUrl();
            String method = pushChannelAdd.getMethod();
            String apiHost = pushChannelAdd.getApiHost();
            String apiAppKey = pushChannelAdd.getApiAppKey();
            String apiAppSecret = pushChannelAdd.getApiAppSecret();
            Map<String, String> headerParams = new HashMap<>(16);
            Map<String, String> queryParams = new HashMap<>(16);
            Map map = new HashMap(16);
            String msgSourceName = msgSourceRepository.selectMsgSourceByChannelName(pushChannelAdd.getDescription());
            //构建参数
            map.put("msgSourceName", msgSourceName);
            List msgList = new ArrayList<>(10);
            MsgVO msgVO = new MsgVO();
            msgVO.setMsgTitle("推送");
            msgVO.setMsgDescription("推送测试");
            List<MsgBody> contents = new ArrayList<>();
            MsgBody msgBody = new MsgBody();
            msgBody.setContent("系统端渠道注册");
            msgBody.setType("TEXT");
            msgBody.setUnitName(null);
            contents.add(msgBody);
            msgVO.setContents(contents);
            msgList.add(msgVO);
            map.put("msgList", msgList);
            // 创建连接
            SyncApiListClient syncClient = SyncApiListClient.newBuilder().appKey(apiAppKey).appSecret(apiAppSecret)
                    .connectionTimeoutMillis(30000L).readTimeoutMillis(30000L).writeTimeoutMillis(30000L).build();
            ApiResponse apiResponse = null;
            try {
                apiResponse = syncClient.apiList(queryParams, headerParams, apiUrl, apiHost, JSONObject.toJSONString(map).getBytes());
                logger.info(" 调用IT服务平台返回结果 {}", apiResponse.getBody());
            } catch (Exception e) {
                logger.info("调用IT服务平台报错 {}", e.toString());
                return 0;
            }
            // 响应状态码，大于等于200小于300表示成功；大于等于400小于500为客户端错误；大于500为服务端错误。
            logger.info("apiDataSourceCheck apiResponse: {}", apiResponse);
            if (MIN_SUCCESS_CODE <= apiResponse.getStatusCode() && apiResponse.getStatusCode() < MAX_SUCCESS_CODE) {
                return 1;
            }
            return 0;
        }
    }

    @Override
    public int testCsbConnection(PushChannelAdd pushChannelAdd) {
        Long pushChannelId = pushChannelAdd.getPushChannelId();
        String tenantCode = pushChannelAdd.getTenantCode();
        PushChannelInfo pushChannelInfo;
        // pushChannelId不为空，说明编辑--联通测试
        if(Objects.nonNull(pushChannelId)){
            pushChannelInfo = PushChannelConverter.INSTANCE.pushChannelAdd2pushChannelInfo(pushChannelAdd);
        }else{
            // pushChannelId为空(两种情况 )，1.渠道外部测试接口，2.新增--联通测试
            pushChannelInfo = pushChannelMap.getChannelInfoByChanneCode(pushChannelAdd.getChannelCode(), tenantCode);
            if (Objects.isNull(pushChannelInfo) || Objects.isNull(pushChannelInfo.getPushChannelId())) {
                pushChannelInfo = PushChannelConverter.INSTANCE.pushChannelAdd2pushChannelInfo(pushChannelAdd);
            }
        }
        if (StringUtils.isBlank(tenantCode)) {
            pushChannelInfo.setTenantCode("MOM");
        }
        // pushChannelMap.addChannelCodeMapCache(pushChannelInfo);
        MsgRecordVO msgRecordVO = buildMockMsgPushDTO(pushChannelInfo);
        // 发送消息
        List<MsgRecordVO> msgRecordVOS = new ArrayList<>();
        msgRecordVOS.add(msgRecordVO);
        try {
            sendStrategyContext.sendMessage(msgRecordVOS);
        } catch (Exception e) {
            logger.error("渠道测试失败，e:{}", e);
            return 0;
        }
        SingleResponse<MsgPushResponseDTO> singleResponse = pushService.processSingleResponse(msgRecordVO);
        if (singleResponse.isSuccess()) {
            return 1;
        } else {
            logger.error("渠道测试失败，e:{}", singleResponse.getErrMessage());
            return 0;
        }
    }

    private MsgRecordVO buildMockMsgPushDTO(PushChannelInfo pushChannelInfo) {
        MsgRecordVO msgRecordVO = new MsgRecordVO();
        msgRecordVO.setUniqueId("test");
        msgRecordVO.setTenantCode(pushChannelInfo.getTenantCode());
        msgRecordVO.setSceneCode("test");
        msgRecordVO.setSceneName("test");
        List<com.aliyun.messagemgt.common.dto.send.MsgBody> contents = new ArrayList<>();
        com.aliyun.messagemgt.common.dto.send.MsgBody msgBody = new com.aliyun.messagemgt.common.dto.send.MsgBody();
        // 目前仅支持TEXT
        msgBody.setType(MediaLimitEnum.TEXT.name().toLowerCase());
        msgBody.setContent("test" + UUID.randomUUID());
        contents.add(msgBody);
        msgRecordVO.setContents(contents);
        List<String> receiveChannelCodes = new ArrayList<>();
        String channelCode = pushChannelInfo.getChannelCode();
        if (StringUtils.isBlank(channelCode)) {
            throw new BaseException(CommonErrorCode.PUSH_CHANNEL_CODE__ISNULL);
        }
        receiveChannelCodes.add(channelCode);
        msgRecordVO.setReceiveChannelCodes(receiveChannelCodes);
        List<Receiver> receivers = new ArrayList<>();
        Receiver receiver = new Receiver();
        if(ChannelTypeEnum.MAIL.getType().equals(pushChannelInfo.getChannelType())){
            msgRecordVO.setSubject("test");
            receiver.setReceiverType(RangeTypeEnum.EMAIL.getCode());
        }else if(ChannelTypeEnum.LANXIN.getType().equals(pushChannelInfo.getChannelType())){
            receiver.setReceiverType(RangeTypeEnum.EMPLOYEECODE.getCode());
        }else{
            receiver.setReceiverType(RangeTypeEnum.PHONE.getCode());
        }
        String receiverType = receiver.getReceiverType();
        SysPropertyDTO sysPropertyDTO = sysPropertyRepository.getByPropertyCode(receiverType);
        if (Objects.isNull(sysPropertyDTO)) {
            throw new BaseException(CommonErrorCode.SYS_PROPERTY_ISNULL);
        }
        if(RangeTypeEnum.EMAIL.getCode().equals(receiverType)){
            receiver.setReceiverEmail(sysPropertyDTO.getPropertyValue());
        }else if(ChannelTypeEnum.LANXIN.getType().equals(pushChannelInfo.getChannelType())){
            receiver.setEmployeeCode(sysPropertyDTO.getPropertyValue());
        }else{
            receiver.setReceiverMobilePhone(sysPropertyDTO.getPropertyValue());
        }
        receivers.add(receiver);
        msgRecordVO.setReceivers(receivers);
        List<PushChannelInfo> pushChannelInfoList = new ArrayList<>();
        pushChannelInfoList.add(pushChannelInfo);
        msgRecordVO.setPushChannelInfoList(pushChannelInfoList);
        msgRecordVO.setFailureResendTimes(1);
        msgRecordVO.setFailureResendInterval(1);
        msgRecordVO.setMsgSourceId(1L);
        msgRecordVO.setMsgSourceCode("test");
        msgRecordVO.setMsgSourceName("test");
        return msgRecordVO;
    }

/*    private boolean checkConnection(PushChannelAdd pushChannelAdd) {
        if (!"dev".equals(dev)){
            return false;
        }
        String apiAppKey = pushChannelAdd.getApiAppKey();
        String apiAppSecret = pushChannelAdd.getApiAppSecret();
        String apiHost = pushChannelAdd.getApiHost();
        String apiUrl = pushChannelAdd.getApiUrl();
//        if (apiAppKey.equals("1545898441509244")&&
//            apiAppSecret.equals("b3b298a894d2481fabe07fdc3946a13c")&&
//            apiHost.equals("7b29082973344ff2b3dc398d91fcf9ac.apigateway.alicloud.ga.sh")&&
//            apiUrl.equals("test/systemApi")){
//            return true;
//        }else {
            return false;
//        }
    }*/


    @Override
    public Object channelsTypes(PushChannelQuery pushChannelQuery) {

        List<ChannelType> channelTypes = new ArrayList<>();
        //获取配置的渠道类型
        if (StringUtils.isBlank(channelType)) {
            List list = ChannelTypeEnum.toList();
            ChannelType channelType;
            for (Object obj : list) {
                Map map = (Map) obj;
                //屏蔽掉暂时未开发完的渠道
                if (!shieldKey.contains(map.get("type").toString())) {
                    channelType = new ChannelType().setValue((int) map.get("type")).setDes(String.valueOf(map.get("name")));
                    channelTypes.add(channelType);
                }
            }
        } else {
            String[] chanelArray = channelType.split(",");
            for (String type : chanelArray) {
                int num = Integer.parseInt(type);
                ChannelTypeEnum channelTypeEnum = ChannelTypeEnum.valueofType(num);
                assert channelTypeEnum != null;
                channelTypes.add(new ChannelType().setValue(channelTypeEnum.getType()).setDes(channelTypeEnum.getName()));
            }
        }
        return channelTypes;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object updateStatus(PushChannelQuery pushChannelQuery) {
        Long pushChannelId = pushChannelQuery.getPushChannelId();
        int useStatus = pushChannelQuery.getUseStatus();

        int i = pushChannelRepository.updateStatus(pushChannelQuery);
        pushChannelMap.setChannelMap();
        return i;
    }

}
